/*
 * ServiceComms.java
 *
 * Created on 29 March 2012, 09:22
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package Simulation.Services.Common;

import Simulation.Messages.Message;
import java.io.IOException;
import java.net.Socket;
import java.net.*;


/**
 *
 * @author aking2
 */
public class ServiceComms {
    
    // Creates a new instance of ServiceComms that reads and writes
    public ServiceComms(String address, int port) {

        try {
            InetAddress ia       = InetAddress.getByName(address);            
            datagramSocket       = new DatagramSocket(port, ia);
            interServiceDestinationSockets   = new InetSocketAddress[MAX_NUM_OF_DESTINATIONS];
            iosDestinationSockets            = new InetSocketAddress[MAX_NUM_OF_IOS_DESTINATIONS];
            recieveBuffer        = new byte[Message.MAX_DATAGRAM_SIZE];
            rcvPacket            = new DatagramPacket(recieveBuffer, Message.MAX_DATAGRAM_SIZE);
            readerInitialised    = true;
        } catch (SocketException ex) {
            ex.printStackTrace();
        } catch (UnknownHostException ex) {
                ex.printStackTrace();
        } 
    }
    
    // Creates a new instance of ServiceComms that just writes    
    public ServiceComms() {

        try {
            datagramSocket                   = new DatagramSocket();
            interServiceDestinationSockets   = new InetSocketAddress[MAX_NUM_OF_DESTINATIONS];
            iosDestinationSockets            = new InetSocketAddress[MAX_NUM_OF_IOS_DESTINATIONS];
            
        } catch (SocketException ex) {
            ex.printStackTrace();
        }
    }    
    

    
    public void SendMessage( Message msg ) {
        
        byte[] msgBytes = msg.CreateDatagramFromMessage(msg);
        DatagramPacket packet = new DatagramPacket(msgBytes, msgBytes.length);
        
        // if the message is destined for the IOS, send it on the IOS ports,
        // othertwise send it to the inter-service message ports.
        if( msg.IsIos(msg)) {
            for( int i=0; i<numOfIosDestinations; i++) {
                try {
                    packet.setSocketAddress(iosDestinationSockets[i]);
                    datagramSocket.send(packet);
                } catch (IOException ex) {
                    ex.printStackTrace();
                }            
            } 
        } else {
            for( int i=0; i<numOfDestinations; i++) {
                try {
                    packet.setSocketAddress(interServiceDestinationSockets[i]);
                    datagramSocket.send(packet);
                } catch (IOException ex) {
                    ex.printStackTrace();
                }            
            }             
        }
    }
    
    public Message RecieveMessage( float timeout ) {
        
        Message result;
        
        if( readerInitialised) {
                    
            try {
                datagramSocket.setSoTimeout((int) (timeout * 1000.0));
                datagramSocket.receive(rcvPacket);
                if( rcvPacket.getLength() >= Message.DATAGRAM_HEADER_SIZE ) {
                    if( recieveBuffer[0] == 'G' &&
                        recieveBuffer[1] == 'B' &&
                        recieveBuffer[2] == 'T' &&
                        recieveBuffer[3] == 'T' ) {
                        result = Message.CreateMessageFromDataGramBytes(recieveBuffer);
                    } else {
                        result = new Message(Message.MessageIdEnum.MESSAGE_BAD_DATA_DETECTED);
                    }
                } else {
                    result = new Message(Message.MessageIdEnum.MESSAGE_BAD_DATA_DETECTED);
                }
            } catch (SocketTimeoutException ex) {  
                result = new Message(Message.MessageIdEnum.MESSAGE_TIMEOUT);
            } catch (SocketException ex) {
                ex.printStackTrace();
                result = new Message(Message.MessageIdEnum.MESSAGE_BAD_DATA_DETECTED);
            } catch (IOException ex) {
                ex.printStackTrace();
                result = new Message(Message.MessageIdEnum.MESSAGE_BAD_DATA_DETECTED);
            }
        } else {
            result = new Message(Message.MessageIdEnum.MESSAGE_NOT_INITIALISED);
        }
        
        return result;    
    }

    
    public void AddInterServiceDestiantion( String address, int port ) {
        if(numOfDestinations < MAX_NUM_OF_DESTINATIONS) {
            try {
                InetAddress ia = InetAddress.getByName(address);
                interServiceDestinationSockets[numOfDestinations] = new InetSocketAddress(ia, port);
                numOfDestinations = numOfDestinations + 1;
            } catch (UnknownHostException ex) {
                ex.printStackTrace();
            } catch (IOException ex) {
                ex.printStackTrace();
            }            
        }
    }
    
    public void AddIosDestiantion( String address, int port ) {
        if(numOfIosDestinations < MAX_NUM_OF_IOS_DESTINATIONS) {
            try {
                InetAddress ia = InetAddress.getByName(address);
                iosDestinationSockets[numOfIosDestinations] = new InetSocketAddress(ia, port);
                numOfIosDestinations = numOfIosDestinations + 1;
            } catch (UnknownHostException ex) {
                ex.printStackTrace();
            } catch (IOException ex) {
                ex.printStackTrace();
            }            
        }
    }          
    
    private DatagramSocket datagramSocket;    
    private static final int MAX_NUM_OF_DESTINATIONS = 10;
    private static final int MAX_NUM_OF_IOS_DESTINATIONS = 10;
    private int numOfDestinations = 0;
    private int numOfIosDestinations = 0;
    private byte[] recieveBuffer;
    DatagramPacket rcvPacket;
    private boolean readerInitialised = false;
    
    private InetSocketAddress[] interServiceDestinationSockets;
    private InetSocketAddress[] iosDestinationSockets;
}
